//-------------------------------------------------------------------------------------------------
// aabb.hpp - Definition of an aabb (axis aligned bounding box) shape for physics operations
// (c) 2009 Simon Miessler
//-------------------------------------------------------------------------------------------------

#pragma once

#include <boost/array.hpp>

#include "math_exports.hpp"
#include "vector3.hpp"
#include "intersectable.hpp"
#include "raycastable.hpp"




namespace math
{
	/**
	 * Axis Aligned Bounding Box in 3d space.
	 */
	template <typename T>
	class aabb
		: public intersectable< aabb<T> >
		, public raycastable< aabb<T>, T >
	{
	public:

		/// The used floating-point type
		typedef T              float_t;

	public:

		/// Position of the bounding box
		vector3<T>   position;

		/// Boundaries of the box, half the length of each edge on the corresponding axis
		vector3<T>   bounds;

	public:

		/**
		 * Create an axis aligned bounding box.
		 * It is located at (0,0,0) with bounds of (1,1,1)
		 */
		aabb()
			: position(0, 0, 0)
			, bounds(1, 1, 1)
		{}

		/**
		 * Create an axis aligned bounding box at the specified position and the specified boundaries.
		 *
		 * @param position   the position of the box in world space
		 * @param bounds     half the length of each edge of the box
		 */
		aabb(const vector3<T>& position, const vector3<T>& bounds)
			: position(position)
			, bounds(bounds)
		{
			assert(bounds.x >= 0);
			assert(bounds.y >= 0);
			assert(bounds.z >= 0);
		}


		/**
		 * Get the most negative corner of the bounding box.
		 */
		vector3<T> negative() const
		{
			return vector3<T>(position.x - bounds.x,
									   position.y - bounds.y,
									   position.z - bounds.z);
		}

		/**
		 * Get the most positive corner of the bounding box
		 */
		vector3<T> positive() const
		{
			return vector3<T>(position.x + bounds.x,
									   position.y + bounds.y,
									   position.z + bounds.z);
		}


		/**
		 * Get a list of all 6 normals of the 6 faces of the AABB.
		 * The normals are in world space.The normals are the same
		 * for \b every AABB instance, so this function could be static
		 * as well, but I decided against it.
		 */
		boost::array<vector3<T>,6> getNormals() const
		{
			boost::array<vector3<T>,6> normals;

			// Both YZ Planes (forward, backward)
			normals[0] = vector3<T>(1, 0, 0);
			normals[1] = vector3<T>(-1, 0, 0);

			// Both XZ Planes (forward, backward)
			normals[2] = vector3<T>(0, 1, 0);
			normals[3] = vector3<T>(0, -1, 0);

			// Both XY Planes (forward, backward)
			normals[4] = vector3<T>(0, 0, 1);
			normals[5] = vector3<T>(0, 0, -1);

			return normals;
		}


		/**
		 * Get a list of all 6 planes that form the AABB.
		 */
		boost::array<plane<T>,6> getPlanes() const
		{
			boost::array<vector3<T>,6> normals = getNormals();
			boost::array<plane<T>,6>   planes;

			vector3<T> n = negative();
			vector3<T> p = positive();

			// Both YZ Planes (forward, backward)
			planes[0] = plane<T>(vector3<T>(p.x, position.y, position.z), normals[0]);
			planes[1] = plane<T>(vector3<T>(n.x, position.y, position.z), normals[1]);

			// Both XZ Planes (forward, backward)
			planes[2] = plane<T>(vector3<T>(position.x, p.y, position.z), normals[2]);
			planes[3] = plane<T>(vector3<T>(position.x, n.y, position.z), normals[3]);

			// Both XY Planes (forward, backward)
			planes[4] = plane<T>(vector3<T>(position.x, position.y, p.z), normals[4]);
			planes[5] = plane<T>(vector3<T>(position.x, position.y, n.z), normals[5]);

			return planes;
		}




		/**
		 * Get a bounding sphere that defines the closest
		 * sphere for this AABB.
		 * The sphere is defined in world coordinates.
		 */
		sphere<T> boundingSphere() const
		{
			return sphere<T>(position, bounds.length());
		}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * In order to compile a binary that actually contains the code for our AABB,
	 * we have to explicitly instantiate the template AABBs and export those:
	 */
	template struct MATH_API intersectable< aabb<float> >;
	template struct MATH_API intersectable< aabb<double> >;
	template struct MATH_API raycastable< aabb<float>, float>;
	template struct MATH_API raycastable< aabb<double>, double>;
	template class MATH_API aabb<float>;
	template class MATH_API aabb<double>;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
